package experiments;

import static de.umass.util.StringUtilities.map;
import gui.KeyUserFetchWindow;
import gui.ResultsWindow;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import utils.Statistics;
import de.umass.lastfm.Artist;
import de.umass.lastfm.CallException;
import de.umass.lastfm.Caller;
import de.umass.lastfm.Chart;
import de.umass.lastfm.ResponseBuilder;
import de.umass.lastfm.Track;
import de.umass.lastfm.User;
import de.umass.lastfm.cache.Cache;
import de.umass.lastfm.cache.FileSystemCache;

public class GlobalStatsARFFOutAL implements ActionListener {

	private KeyUserFetchWindow kufw;
	private String key;
	private String userName;
	
	private final int TOP_TRACKS_NUMBER = 10;
	private final boolean USING_KMEANS = false;
	
	public GlobalStatsARFFOutAL(String key) {
		this.key = key;
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if(kufw.getUser().isEmpty()) return;
		kufw.dispose();
		
		//Setup the cache directory.
	    File dataFolder = new File("./cache");
	    if (!dataFolder.isDirectory()) {
	        dataFolder.mkdir();
	    }
	    Caller.getInstance().setCache(new FileSystemCache(dataFolder));
	    
	    //Fetches the user name.
	    userName = kufw.getUser();
	    
	    Collection<Artist> topArtists;
	    //Calls last.fm.
	    if(userName.toLowerCase().equals("global stats")) {
	    	//50 top artists + 50 hyped artists.
	    	topArtists = Chart.getTopArtists(key).getPageResults();
	    	topArtists.addAll(Chart.getHypedArtists(key).getPageResults());
	    }
	    else
	    	topArtists = User.getTopArtists(userName, key);
	    
	    Map<Artist, Collection<Track>> topTracks = new HashMap<Artist, Collection<Track>>();
	    
	    for (Artist artist : topArtists) {
	    	try {
	    		//This is a last.fm requirement. Only 5 calls per second to the API.
	    		String cacheEntryName = Cache.createCacheEntryName("artist.getTopTracks", map("artist", artist.getName(), "limit", Integer.toString(TOP_TRACKS_NUMBER)));
				if(!Caller.getInstance().getCache().contains(cacheEntryName))
						Thread.sleep(200);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
	    	
	    	Collection<Track> topTracksTemp = null;
	    	try {
	    		//Calls last.fm. Building the call manually so I can specify a limit.
	    		topTracksTemp = ResponseBuilder.buildCollection(Caller.getInstance().call("artist.getTopTracks", key, "artist", artist.getName(), "limit", Integer.toString(TOP_TRACKS_NUMBER)), Track.class);
	    	}
	    	catch(CallException ce) {
	    		System.out.println("Artist \"" + artist.getName() + "\" could not have it's top tracks fetched (XML parse error)");
	    	}
	    	if(topTracksTemp != null)
	    		topTracks.put(artist, topTracksTemp);
	    }
	    
	    if(USING_KMEANS)
	    	arffOutKMeans(topArtists, topTracks);
	    else
	    	arffOutDecTree(topArtists, topTracks);
	}

	public void registerWindow(KeyUserFetchWindow kufw) {
		this.kufw = kufw;
	}

	private void arffOutKMeans(Collection<Artist> topArtists, Map<Artist, Collection<Track>> topTracks) {
		ResultsWindow rw = new ResultsWindow("Top artists for " + userName);
	    Statistics statistics;
	    List<Double> data = new ArrayList<Double>();
	    double sum;
	    rw.println("%Title: One-hit-wonder meter analysis.\n"); 
	    rw.println("@RELATION ohw\n");
	    rw.println("@ATTRIBUTE value NUMERIC");
	    rw.println("@ATTRIBUTE listeners NUMERIC");
	    rw.println("@ATTRIBUTE playcount NUMERIC\n");
	    rw.println("@DATA");
	    for(Artist artist : topArtists) {
	    	sum = 0;
	    	data.clear();
	    	if(topTracks.containsKey(artist)) {
		    	for(Track track : topTracks.get(artist)) {
		    		data.add((double)track.getPlaycount());
		    		sum+=track.getPlaycount();
		    	}
		    	statistics = new Statistics(data.toArray(new Double[data.size()]));
		    	double value = statistics.getStdDev()/sum;
		    	rw.print(value + ",");
		    	rw.print(artist.getListeners() + ",");
		    	rw.println(artist.getPlaycount() + "");
	    	}
	    }
	    rw.adjustSize();
	}
	
	private void arffOutDecTree(Collection<Artist> topArtists, Map<Artist, Collection<Track>> topTracks) {
		ResultsWindow rw = new ResultsWindow("Top artists for " + userName);
	    Statistics statistics;
	    List<Double> data = new ArrayList<Double>();
	    rw.println("%Title: One-hit-wonder meter analysis.\n"); 
	    rw.println("@RELATION ohw\n");
	    rw.println("@ATTRIBUTE highOutliers NUMERIC");
	    rw.println("@ATTRIBUTE midOutliers NUMERIC");
	    rw.println("@ATTRIBUTE lowOutliers NUMERIC");
	    rw.println("@ATTRIBUTE aboveAvgCount NUMERIC");
	    rw.println("@ATTRIBUTE rank {OH, MH, WD, VW}\n");
	    rw.println("@DATA");
	    
	    for(Artist artist : topArtists) {
	    	data.clear();
	    	if(topTracks.containsKey(artist)) {
		    	
	    		//Too few tracks.
	    		if(topTracks.get(artist).size() < TOP_TRACKS_NUMBER)
	    			continue;
	    		
		    	for(Track track : topTracks.get(artist)) {
		    		data.add((double)track.getPlaycount());
		    	}
		    	
		    	//Some kind of fetch problem, ignoring...
		    	if(data.isEmpty())
		    		continue;
		    	
		    	statistics = new Statistics(data.toArray(new Double[data.size()]));
		    	double mean = statistics.getMean();
		    	
		    	double highOutlierLimit = mean + 2.5*mean;
		    	double midOutlierLimit = mean + 1.25*mean;
		    	double lowOutlierLimit = mean + 0.5*mean;
		    	double aboveAvg = mean;
		    	
		    	int highOutlierCount = 0;
		    	int midOutlierCount = 0;
		    	int lowOutlierCount = 0;
		    	int aboveAvgCount = 0;
		    	
		    	for(Track track : topTracks.get(artist)) {
		    		if(track.getPlaycount() > highOutlierLimit) {
		    			highOutlierCount++;
		    			midOutlierCount++;
		    			lowOutlierCount++;
		    			aboveAvgCount++;
		    		}
		    		else if (track.getPlaycount() > midOutlierLimit) {
		    			midOutlierCount++;
		    			lowOutlierCount++;
		    			aboveAvgCount++;
		    		}
		    		else if(track.getPlaycount() > lowOutlierLimit) {
		    			lowOutlierCount++;
		    			aboveAvgCount++;
		    		}
		    		else if(track.getPlaycount() > aboveAvg)
		    			aboveAvgCount++;
		    	}
		    	
		    	rw.println(highOutlierCount + "," + midOutlierCount + "," + lowOutlierCount + "," + aboveAvgCount + ",");
		    	String artistName = artist.getName().replace(" ", "+");
			    rw.println("http://www.lastfm.com.br/music/" + artistName + "/+tracks");
	    	}
	    }	
	    rw.adjustSize();
	}
}
